package com.castle.fortress.admin.shiro.diversity;

import cn.hutool.json.JSONUtil;
import com.castle.fortress.admin.core.constants.GlobalConstants;
import com.castle.fortress.admin.shiro.entity.CastleUserDetail;
import com.castle.fortress.admin.utils.RedisUtils;
import com.castle.fortress.admin.utils.SpringUtils;
import com.castle.fortress.admin.utils.TokenUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.respcode.GlobalRespCode;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.logging.log4j.util.Strings;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * jwt token 过滤器
 * @author castle
 */
public class JwtFilter extends AuthenticatingFilter {


    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws ExpiredJwtException, Exception {
        String token = getToken(request);
        if (Strings.isEmpty(token)) {
            System.out.println("Token is empty from request");
            return null;
        }

        try {
            // 检查是否需要从Redis获取真实token
            if (!token.startsWith("Bearer ")) {
                RedisUtils redisUtils = SpringUtils.getBean(RedisUtils.class);
                Object tokenRedis = redisUtils.get(token);
                if (tokenRedis != null) {
                    Map<String, String> tokenMap = (Map<String, String>) tokenRedis;
                    if ("login".equals(tokenMap.get("status"))) {
                        token = tokenMap.get("token");
                        System.out.println("Retrieved token from Redis");
                    } else {
                        System.out.println("Token in Redis has invalid status");
                        return null;
                    }
                }
            }

            // 解析token
            CastleUserDetail userDetail = TokenUtil.parseToken(token);
            if (userDetail == null) {
                System.out.println("User detail is null after parsing token");
                return null;
            }
            return new CastleUserToken(userDetail);
        } catch (ExpiredJwtException e) {
            System.out.println("Token已过期: " + e.getMessage());
            throw e;
        } catch (Exception e) {
            System.out.println("Token解析异常: " + e.getMessage());
            e.printStackTrace();
            throw e;
        }
    }


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //获取请求token，如果token不存在，直接返回401
        String token = getToken(servletRequest);
        //设置响应头
        HttpServletResponse res=(HttpServletResponse) servletResponse;
        res.setHeader("Access-Control-Allow-Credentials", "true");
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "*");
        res.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");
        res.setHeader("Access-Control-Expose-Headers", "*");
        if(Strings.isEmpty(token)){
            res.setContentType("application/json;charset=utf-8");
            res.getWriter().println(JSONUtil.toJsonStr(RespBody.fail(GlobalRespCode.NO_LOGIN_ERROR)));
            return false;
        }
        boolean flag =false;
        try {
            flag = executeLogin(servletRequest, servletResponse);
        }catch (ExpiredJwtException e){
            res.setContentType("application/json;charset=utf-8");
            res.getWriter().println(JSONUtil.toJsonStr(RespBody.fail(GlobalRespCode.TOKEN_EXPIRED_ERROR)));
            return false;
        }catch (Exception e) {
            e.printStackTrace();
            res.setContentType("application/json;charset=utf-8");
            res.getWriter().println(JSONUtil.toJsonStr(RespBody.fail(GlobalRespCode.TOKEN_INVALID_ERROR)));
            return false;
        }
        return flag;
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws ExpiredJwtException,Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        } else {
            try {
                Subject subject = this.getSubject(request, response);
                subject.login(token);
                return this.onLoginSuccess(token, subject, request, response);
            } catch (AuthenticationException var5) {
                return this.onLoginFailure(token, var5, request, response);
            }
        }
    }

    /**
     * 获取请求的token
     */
    private String getToken(ServletRequest request){
        HttpServletRequest httpRequest=(HttpServletRequest)request;
        //从header中获取token
        String token = httpRequest.getHeader(GlobalConstants.TOKEN_HEADER_KEY);

        // 如果header中没有，尝试从参数中获取
        if(Strings.isEmpty(token)) {
            token = httpRequest.getParameter(GlobalConstants.TOKEN_HEADER_KEY);
            System.out.println("Token not found in header, checking parameters: " + (token != null ? "found" : "not found"));
        }

        // 确保token格式正确，需要包含"Bearer "前缀
        if(token != null && !token.startsWith("Bearer ") && token.length() > 0) {
            if(token.contains(".") && token.split("\\.").length == 3) {
                token = "Bearer " + token;
                System.out.println("Added Bearer prefix to valid JWT token");
            } else {
                // 这可能是一个Redis缓存的key，不添加Bearer前缀
                System.out.println("Token doesn't look like JWT, not adding Bearer prefix");
            }
        }

        return token;
    }
}
